package fr.ensisa.sunspot.actor.osc;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.Vector;

import osc.cs.PacketParser;
import osc.cs.PacketUnparser;
import fr.ensisa.sunspot.actor.Template;
import fr.ensisa.sunspot.event.IEvent;

public class OscSocket implements Runnable {
	private Socket s;
	private PacketParser parser;
	private PacketUnparser unparser;
	private DataInputStream dis;
	private DataOutputStream dos;
	private Vector<IEvent> packets;
	private Vector<IEvent> buffer;

	public OscSocket(Socket s) {
		this.init(s);
	}
	
	public OscSocket(String ip, int port, Template t) {
		try {
			Socket s = new Socket(ip, port);
			this.init(s);
			this.sendTempalte(t);
		}
		catch (UnknownHostException e) {
			e.printStackTrace();
		} 
		catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void init(Socket s) {
		this.parser = new PacketParser();
		this.unparser = new PacketUnparser();
		
		this.s = s;
		
		try {
			this.dis = new DataInputStream(new BufferedInputStream(this.s.getInputStream()));
			this.dos = new DataOutputStream(new BufferedOutputStream(this.s.getOutputStream()));
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void sendTempalte(Template t) {
		try {
			ObjectOutputStream oos = new ObjectOutputStream(this.dos);
			oos.writeObject(t);
			oos.flush();
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}

	public Template readTemplate() {
		Template t = null;
		
		try {
			ObjectInputStream ois = new ObjectInputStream(this.dis);
			t = (Template) ois.readObject();
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
		return t;
	}
	
	public void sendEvent(IEvent event) {
		try {
			ByteBuffer buffer = this.unparser.unparse(OscTranslator.EventToOscPacket(event));
			this.dos.writeInt(buffer.capacity());
			this.dos.flush();
			
			this.dos.writeInt(buffer.limit());
			this.dos.flush();
			
			byte[] bufferTmp = new byte[buffer.limit()];
			buffer.get(bufferTmp);
			
			this.dos.write(bufferTmp);
			this.dos.flush();
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public synchronized void addEvent(IEvent event) {
		this.getPackets().add(event);
	}
	
	public synchronized Vector<IEvent> getPackets() {
		if(this.packets == null)
			this.packets = new Vector<IEvent>(10);
		
		return this.packets;
	}
	
	public Vector<IEvent> getBuffer() {
		this.moveEventToBuffers();
		return this.buffer;
	}
	
	private synchronized void moveEventToBuffers() {
		this.buffer = this.getPackets();
		this.packets = null;
	}
	
	public boolean isEmpty() {
		return this.getPackets().isEmpty();
	}
	
	public boolean isClosed() {
		return this.s.isClosed();
	}

	@Override
	public void run() {
		try {
			while(true) {
				int capacity = this.dis.readInt();				
				int limit = this.dis.readInt();
				
				ByteBuffer buffer = ByteBuffer.allocateDirect(capacity);
				byte[] bufferTmp = new byte[limit];
				this.dis.read(bufferTmp);
				
				buffer.put(bufferTmp);
				buffer.flip();
				IEvent event = OscTranslator.OscPacketToEvent(this.parser.parse(buffer));
				
				if(event != null)
					this.addEvent(event);
			}
		}
		catch (IOException e) {
		}
		catch(Exception e) {
		}
		finally {
			try {
				this.s.close();
			}
			catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}
